library(tidyverse)
library(lubridate)
library(ggplot2)

url <- 'http://berkeleyearth.lbl.gov/air-quality/maps/cities/Thailand/Chiang_Mai/Chiang_Mai.txt'
df <- read_tsv(url,skip=10, col_names = FALSE)
Parsed with column specification:
cols(
  X1 = col_double(),
  X2 = col_double(),
  X3 = col_double(),
  X4 = col_double(),
  X5 = col_double(),
  X6 = col_double(),
  X7 = col_double()
)
df

—— Summarized Data (Reserved) ——

—— Visualized Data ——

## entire data

by date for each month

By month for each year

df %>% 
  mutate(year = year(local_dt),
         month = month(local_dt),
         date = date(local_dt)) %>%
  group_by(year,month) %>%
  summarize(avg_pm = mean(PM2_5)) %>%
  ggplot(aes(month, y = avg_pm , color = as.factor(year)))+geom_line()

## create heat map

df %>% 
  mutate(date = date(local_dt)) %>%
  group_by(date) %>%
  summarize(avg_pm = mean(PM2_5)) %>%
  mutate(day = day(date),
         month = month(date),
         year = year(date)) %>%
  ggplot(aes(day, y=month, fill=avg_pm)) + geom_tile()

## add factor to map, make it display by day

df %>% 
  mutate(date = date(local_dt)) %>%
  group_by(date) %>%
  summarize(avg_pm = mean(PM2_5)) %>%
  mutate(day = as.factor(day(date)),
         month = as.factor(month(date)),
         year = as.factor(year(date))) %>%
  ggplot(aes(day, y=month, fill=avg_pm)) + geom_tile()

## add border and set a color

df %>% 
  mutate(date = date(local_dt)) %>%
  group_by(date) %>%
  summarize(avg_pm = mean(PM2_5)) %>%
  mutate(day = as.factor(day(date)),
         month = as.factor(month(date)),
         year = as.factor(year(date))) %>%
  ggplot(aes(day, y=month, fill=avg_pm)) +
  geom_tile(color="white") +
  scale_fill_gradient(low = "green" , high = 'red')

adjust cell scale

df %>% 
  mutate(date = date(local_dt)) %>%
  group_by(date) %>%
  summarize(avg_pm = mean(PM2_5)) %>%
  mutate(day = as.factor(day(date)),
         month = as.factor(month(date)),
         year = as.factor(year(date))) %>%
  ggplot(aes(day, y=month, fill=avg_pm)) +
  geom_tile(color="white") +
  scale_fill_gradient(low = "green" , high = 'red') +
  coord_equal()

set interval factor for avg pm 2.5

df %>% 
  mutate(date = date(local_dt)) %>%
  group_by(date) %>%
  summarize(avg_pm = mean(PM2_5)) %>%
  mutate(day = as.factor(day(date)),
         month = as.factor(month(date)),
         year = as.factor(year(date))) %>%
  mutate(pm_lv = cut(avg_pm, ## create new column to classify level of smog
         breaks = c(0,12,35.4,55.4,150.4,250.4,350.4),
         labels = c('good','moderate','unheathy for sensitive groups'
                    ,'unheathy','very unheathy','hazardous'))) %>%
  ggplot(aes(day, y=month, fill=pm_lv)) + ## replace avg_pm with pm_level
  geom_tile(color="white") +
  scale_fill_manual(values = c('green','yellow','orange','red','magenta', 'violet'))+
  coord_equal()

separate by year

df %>% 
  filter(year(local_dt) > 2015) %>%
  mutate(date = date(local_dt)) %>%
  group_by(date) %>%
  summarize(avg_pm = mean(PM2_5)) %>%
  mutate(day = as.factor(day(date)),
         month = as.factor(month(date)),
         year = as.factor(year(date))) %>%
  mutate(pm_lv = cut(avg_pm, 
         breaks = c(0,12,35.4,55.4,150.4,250.4,350.4),
         labels = c('good','moderate','unheathy for sensitive groups'
                    ,'unheathy','very unheathy','hazardous'))) %>%
  ggplot(aes(day, y=month, fill=pm_lv)) + 
  geom_tile(color="white") +
  facet_grid(year~.) + ## separate by year
  scale_fill_manual(values = c('green','yellow','orange','red','magenta', 'violet'))+
  coord_equal()

reverse month -> make less value on top

df %>% 
  filter(year(local_dt) > 2015) %>%
  mutate(date = date(local_dt)) %>%
  group_by(date) %>%
  summarize(avg_pm = mean(PM2_5)) %>%
  mutate(day = as.factor(day(date)),
         month = fct_rev(as_factor(month(date))),  ## remove as.factor and add fct_rev() to month and make it factor using as_factor
         year = as.factor(year(date))) %>%
  mutate(pm_lv = cut(avg_pm, 
         breaks = c(0,12,35.4,55.4,150.4,250.4,350.4),
         labels = c('good','moderate','unheathy for sensitive groups'
                    ,'unheathy','very unheathy','hazardous'))) %>%
  ggplot(aes(day, y=month, fill=pm_lv)) + 
  xlab("Day") + ylab("Month") +
  geom_tile(color="white") +
  facet_grid(year~.) + 
  scale_fill_manual(values = c('green','yellow','orange','red','magenta', 'violet'))+
  coord_equal()

add tooltip

tt <- df %>%  ## put ggplot in to tt (tooltip)
  filter(year(local_dt) > 2017) %>%
  mutate(date = date(local_dt)) %>%
  group_by(date) %>%
  summarize(avg_pm = mean(PM2_5)) %>%
  mutate(day = as.factor(day(date)),
         month = fct_rev(as_factor(month(date))),  
         year = as.factor(year(date))) %>%
  mutate(pm_lv = cut(avg_pm, 
         breaks = c(0,12,35.4,55.4,150.4,250.4,350.4),
         labels = c('good','moderate','unheathy for sensitive groups'
                    ,'unheathy','very unheathy','hazardous'))) %>%
  ggplot(aes(day, y=month, fill=pm_lv)) + 
  xlab("Day") + ylab("Month") +
  geom_tile(color="white") +
  facet_grid(year~.) + 
  scale_fill_manual(values = c('green','yellow','orange','red','magenta', 'violet'))+
  coord_equal()

ggplotly(tt) # display ggplot with tooltip
LS0tDQp0aXRsZTogIlBNIDIuNSBpbiBHZXJtYW55Ig0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KYGBge3J9DQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkobHVicmlkYXRlKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KYGBgDQpgYGB7cn0NCiNpbXBvcnQgZGF0YSBmcm9tIHVybCBvciBmaWxlDQoNCiMgQ2hpYW5nIE1haSAtIGh0dHA6Ly9iZXJrZWxleWVhcnRoLmxibC5nb3YvYWlyLXF1YWxpdHkvbWFwcy9jaXRpZXMvVGhhaWxhbmQvQ2hpYW5nX01haS9DaGlhbmdfTWFpLnR4dA0KIyBCYW5na29rIC0gaHR0cDovL2JlcmtlbGV5ZWFydGgubGJsLmdvdi9haXItcXVhbGl0eS9tYXBzL2NpdGllcy9UaGFpbGFuZC9CYW5na29rL0Jhbmdrb2sudHh0DQoNCnVybCA8LSAnaHR0cDovL2JlcmtlbGV5ZWFydGgubGJsLmdvdi9haXItcXVhbGl0eS9tYXBzL2NpdGllcy9UaGFpbGFuZC9DaGlhbmdfTWFpL0NoaWFuZ19NYWkudHh0Jw0KZGYgPC0gcmVhZF90c3YodXJsLHNraXA9MTAsIGNvbF9uYW1lcyA9IEZBTFNFKQ0KZGYNCmBgYA0KDQpgYGB7cn0NCmNvbG5hbWVzKGRmKSA8LSBjKCdZZWFyJywnTW9udGgnLCdEYXknLCdIb3VyJywnUE0yXzUnLCdYNicsJ1g3JykNCmRmDQpgYGANCg0KYGBge3J9DQpkZiA8LSBkZiAlPiUNCiAgc2VsZWN0KFllYXI6UE0yXzUpDQpkZg0KYGBgDQoNCmBgYHtyfQ0KZGYgPC0gZGYgJT4lIA0KICBtdXRhdGUoZGF0ZV90aW1lID0gSVNPZGF0ZShZZWFyLE1vbnRoLERheSxIb3VyKSwNCiAgICAgICAgIGxvY2FsX2R0ID0gZGF0ZV90aW1lK2hvdXJzKDcpLA0KICAgICAgICAgbG9jYWxfaHIgPSBob3VyKGxvY2FsX2R0KSkNCmRmDQpgYGANCiAtLS0tLS0gU3VtbWFyaXplZCBEYXRhIChSZXNlcnZlZCkgLS0tLS0tDQoNCmBgYHtyfQ0KZGYgJT4lIA0KICBtdXRhdGUoeWVhciA9IHllYXIobG9jYWxfZHQpLA0KICAgICAgICAgbW9udGggPSBtb250aChsb2NhbF9kdCksDQogICAgICAgICBkYXRlID0gZGF0ZShsb2NhbF9kdCkpICU+JQ0KICBncm91cF9ieShkYXRlKSAlPiUNCiAgc3VtbWFyaXplKGF2Z19wbSA9IG1lYW4oUE0yXzUpKQ0KYGBgDQogLS0tLS0tIFZpc3VhbGl6ZWQgRGF0YSAtLS0tLS0NCiANCiAjIyBlbnRpcmUgZGF0YQ0KYGBge3J9DQpkZiAlPiUgDQogIG11dGF0ZSh5ZWFyID0geWVhcihsb2NhbF9kdCksDQogICAgICAgICBtb250aCA9IG1vbnRoKGxvY2FsX2R0KSwNCiAgICAgICAgIGRhdGUgPSBkYXRlKGxvY2FsX2R0KSkgJT4lDQogIGdyb3VwX2J5KGRhdGUpICU+JQ0KICBzdW1tYXJpemUoYXZnX3BtID0gbWVhbihQTTJfNSkpICU+JQ0KICBnZ3Bsb3QoYWVzKGRhdGUsIHkgPSBhdmdfcG0gLCBjb2xvciA9IGFzLmZhY3Rvcih5ZWFyKGRhdGUpKSkpK2dlb21fbGluZSgpK2dlb21fc21vb3RoKCkNCmBgYA0KDQojIyBieSBkYXRlIGZvciBlYWNoIG1vbnRoDQpgYGB7cn0NCmRmICU+JSANCiAgbXV0YXRlKHllYXIgPSB5ZWFyKGxvY2FsX2R0KSwNCiAgICAgICAgIG1vbnRoID0gbW9udGgobG9jYWxfZHQpLA0KICAgICAgICAgZGF0ZSA9IGRhdGUobG9jYWxfZHQpKSAlPiUNCiAgZ3JvdXBfYnkoeWVhcixtb250aCxkYXRlKSAlPiUNCiAgc3VtbWFyaXplKGF2Z19wbSA9IG1lYW4oUE0yXzUpKSAlPiUNCiAgZ2dwbG90KGFlcyhkYXRlLCB5ID0gYXZnX3BtICwgY29sb3IgPSBhcy5mYWN0b3IobW9udGgpKSkrZ2VvbV9saW5lKCkNCmBgYA0KDQojIyBCeSBtb250aCBmb3IgZWFjaCB5ZWFyDQpgYGB7cn0NCmRmICU+JSANCiAgbXV0YXRlKHllYXIgPSB5ZWFyKGxvY2FsX2R0KSwNCiAgICAgICAgIG1vbnRoID0gbW9udGgobG9jYWxfZHQpLA0KICAgICAgICAgZGF0ZSA9IGRhdGUobG9jYWxfZHQpKSAlPiUNCiAgZ3JvdXBfYnkoeWVhcixtb250aCkgJT4lDQogIHN1bW1hcml6ZShhdmdfcG0gPSBtZWFuKFBNMl81KSkgJT4lDQogIGdncGxvdChhZXMobW9udGgsIHkgPSBhdmdfcG0gLCBjb2xvciA9IGFzLmZhY3Rvcih5ZWFyKSkpK2dlb21fbGluZSgpDQpgYGANCiANCiAjIyBjcmVhdGUgaGVhdCBtYXANCmBgYHtyfQ0KZGYgJT4lIA0KICBtdXRhdGUoZGF0ZSA9IGRhdGUobG9jYWxfZHQpKSAlPiUNCiAgZ3JvdXBfYnkoZGF0ZSkgJT4lDQogIHN1bW1hcml6ZShhdmdfcG0gPSBtZWFuKFBNMl81KSkgJT4lDQogIG11dGF0ZShkYXkgPSBkYXkoZGF0ZSksDQogICAgICAgICBtb250aCA9IG1vbnRoKGRhdGUpLA0KICAgICAgICAgeWVhciA9IHllYXIoZGF0ZSkpICU+JQ0KICBnZ3Bsb3QoYWVzKGRheSwgeT1tb250aCwgZmlsbD1hdmdfcG0pKSArIGdlb21fdGlsZSgpDQpgYGANCiANCiAjIyBhZGQgZmFjdG9yIHRvIG1hcCwgbWFrZSBpdCBkaXNwbGF5IGJ5IGRheQ0KIA0KYGBge3J9DQpkZiAlPiUgDQogIG11dGF0ZShkYXRlID0gZGF0ZShsb2NhbF9kdCkpICU+JQ0KICBncm91cF9ieShkYXRlKSAlPiUNCiAgc3VtbWFyaXplKGF2Z19wbSA9IG1lYW4oUE0yXzUpKSAlPiUNCiAgbXV0YXRlKGRheSA9IGFzLmZhY3RvcihkYXkoZGF0ZSkpLA0KICAgICAgICAgbW9udGggPSBhcy5mYWN0b3IobW9udGgoZGF0ZSkpLA0KICAgICAgICAgeWVhciA9IGFzLmZhY3Rvcih5ZWFyKGRhdGUpKSkgJT4lDQogIGdncGxvdChhZXMoZGF5LCB5PW1vbnRoLCBmaWxsPWF2Z19wbSkpICsgZ2VvbV90aWxlKCkNCmBgYA0KIA0KICMjIGFkZCBib3JkZXIgYW5kIHNldCBhIGNvbG9yDQpgYGB7cn0NCmRmICU+JSANCiAgbXV0YXRlKGRhdGUgPSBkYXRlKGxvY2FsX2R0KSkgJT4lDQogIGdyb3VwX2J5KGRhdGUpICU+JQ0KICBzdW1tYXJpemUoYXZnX3BtID0gbWVhbihQTTJfNSkpICU+JQ0KICBtdXRhdGUoZGF5ID0gYXMuZmFjdG9yKGRheShkYXRlKSksDQogICAgICAgICBtb250aCA9IGFzLmZhY3Rvcihtb250aChkYXRlKSksDQogICAgICAgICB5ZWFyID0gYXMuZmFjdG9yKHllYXIoZGF0ZSkpKSAlPiUNCiAgZ2dwbG90KGFlcyhkYXksIHk9bW9udGgsIGZpbGw9YXZnX3BtKSkgKw0KICBnZW9tX3RpbGUoY29sb3I9IndoaXRlIikgKw0KICBzY2FsZV9maWxsX2dyYWRpZW50KGxvdyA9ICJncmVlbiIgLCBoaWdoID0gJ3JlZCcpDQpgYGANCiANCiMjIGFkanVzdCBjZWxsIHNjYWxlDQpgYGB7ciwgZmlnLmhpZ2h0PTIsIGZpZy53aWR0aD04fSANCmRmICU+JSANCiAgbXV0YXRlKGRhdGUgPSBkYXRlKGxvY2FsX2R0KSkgJT4lDQogIGdyb3VwX2J5KGRhdGUpICU+JQ0KICBzdW1tYXJpemUoYXZnX3BtID0gbWVhbihQTTJfNSkpICU+JQ0KICBtdXRhdGUoZGF5ID0gYXMuZmFjdG9yKGRheShkYXRlKSksDQogICAgICAgICBtb250aCA9IGFzLmZhY3Rvcihtb250aChkYXRlKSksDQogICAgICAgICB5ZWFyID0gYXMuZmFjdG9yKHllYXIoZGF0ZSkpKSAlPiUNCiAgZ2dwbG90KGFlcyhkYXksIHk9bW9udGgsIGZpbGw9YXZnX3BtKSkgKw0KICBnZW9tX3RpbGUoY29sb3I9IndoaXRlIikgKw0KICBzY2FsZV9maWxsX2dyYWRpZW50KGxvdyA9ICJncmVlbiIgLCBoaWdoID0gJ3JlZCcpICsNCiAgY29vcmRfZXF1YWwoKQ0KYGBgDQojIyBzZXQgaW50ZXJ2YWwgZmFjdG9yIGZvciBhdmcgcG0gMi41DQoNCmBgYHtyfQ0KZGYgJT4lIA0KICBtdXRhdGUoZGF0ZSA9IGRhdGUobG9jYWxfZHQpKSAlPiUNCiAgZ3JvdXBfYnkoZGF0ZSkgJT4lDQogIHN1bW1hcml6ZShhdmdfcG0gPSBtZWFuKFBNMl81KSkgJT4lDQogIG11dGF0ZShkYXkgPSBhcy5mYWN0b3IoZGF5KGRhdGUpKSwNCiAgICAgICAgIG1vbnRoID0gYXMuZmFjdG9yKG1vbnRoKGRhdGUpKSwNCiAgICAgICAgIHllYXIgPSBhcy5mYWN0b3IoeWVhcihkYXRlKSkpICU+JQ0KICBtdXRhdGUocG1fbHYgPSBjdXQoYXZnX3BtLCAjIyBjcmVhdGUgbmV3IGNvbHVtbiB0byBjbGFzc2lmeSBsZXZlbCBvZiBzbW9nDQogICAgICAgICBicmVha3MgPSBjKDAsMTIsMzUuNCw1NS40LDE1MC40LDI1MC40LDM1MC40KSwNCiAgICAgICAgIGxhYmVscyA9IGMoJ2dvb2QnLCdtb2RlcmF0ZScsJ3VuaGVhdGh5IGZvciBzZW5zaXRpdmUgZ3JvdXBzJw0KICAgICAgICAgICAgICAgICAgICAsJ3VuaGVhdGh5JywndmVyeSB1bmhlYXRoeScsJ2hhemFyZG91cycpKSkgJT4lDQogIGdncGxvdChhZXMoZGF5LCB5PW1vbnRoLCBmaWxsPXBtX2x2KSkgKyAjIyByZXBsYWNlIGF2Z19wbSB3aXRoIHBtX2xldmVsDQogIGdlb21fdGlsZShjb2xvcj0id2hpdGUiKSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoJ2dyZWVuJywneWVsbG93Jywnb3JhbmdlJywncmVkJywnbWFnZW50YScsICd2aW9sZXQnKSkrDQogIGNvb3JkX2VxdWFsKCkNCmBgYA0KDQojIyBzZXBhcmF0ZSBieSB5ZWFyDQpgYGB7cixmaWcuaGlnaHQ9MTIsIGZpZy53aWR0aD0xNn0NCmRmICU+JSANCiAgZmlsdGVyKHllYXIobG9jYWxfZHQpID4gMjAxNSkgJT4lDQogIG11dGF0ZShkYXRlID0gZGF0ZShsb2NhbF9kdCkpICU+JQ0KICBncm91cF9ieShkYXRlKSAlPiUNCiAgc3VtbWFyaXplKGF2Z19wbSA9IG1lYW4oUE0yXzUpKSAlPiUNCiAgbXV0YXRlKGRheSA9IGFzLmZhY3RvcihkYXkoZGF0ZSkpLA0KICAgICAgICAgbW9udGggPSBhcy5mYWN0b3IobW9udGgoZGF0ZSkpLA0KICAgICAgICAgeWVhciA9IGFzLmZhY3Rvcih5ZWFyKGRhdGUpKSkgJT4lDQogIG11dGF0ZShwbV9sdiA9IGN1dChhdmdfcG0sIA0KICAgICAgICAgYnJlYWtzID0gYygwLDEyLDM1LjQsNTUuNCwxNTAuNCwyNTAuNCwzNTAuNCksDQogICAgICAgICBsYWJlbHMgPSBjKCdnb29kJywnbW9kZXJhdGUnLCd1bmhlYXRoeSBmb3Igc2Vuc2l0aXZlIGdyb3VwcycNCiAgICAgICAgICAgICAgICAgICAgLCd1bmhlYXRoeScsJ3ZlcnkgdW5oZWF0aHknLCdoYXphcmRvdXMnKSkpICU+JQ0KICBnZ3Bsb3QoYWVzKGRheSwgeT1tb250aCwgZmlsbD1wbV9sdikpICsgDQogIGdlb21fdGlsZShjb2xvcj0id2hpdGUiKSArDQogIGZhY2V0X2dyaWQoeWVhcn4uKSArICMjIHNlcGFyYXRlIGJ5IHllYXINCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygnZ3JlZW4nLCd5ZWxsb3cnLCdvcmFuZ2UnLCdyZWQnLCdtYWdlbnRhJywgJ3Zpb2xldCcpKSsNCiAgY29vcmRfZXF1YWwoKQ0KDQpgYGANCiMjIHJldmVyc2UgbW9udGggLT4gbWFrZSBsZXNzIHZhbHVlIG9uIHRvcA0KYGBge3IsZmlnLmhpZ2h0PTEyLCBmaWcud2lkdGg9MTZ9DQpkZiAlPiUgDQogIGZpbHRlcih5ZWFyKGxvY2FsX2R0KSA+IDIwMTUpICU+JQ0KICBtdXRhdGUoZGF0ZSA9IGRhdGUobG9jYWxfZHQpKSAlPiUNCiAgZ3JvdXBfYnkoZGF0ZSkgJT4lDQogIHN1bW1hcml6ZShhdmdfcG0gPSBtZWFuKFBNMl81KSkgJT4lDQogIG11dGF0ZShkYXkgPSBhcy5mYWN0b3IoZGF5KGRhdGUpKSwNCiAgICAgICAgIG1vbnRoID0gZmN0X3Jldihhc19mYWN0b3IobW9udGgoZGF0ZSkpKSwgICMjIHJlbW92ZSBhcy5mYWN0b3IgYW5kIGFkZCBmY3RfcmV2KCkgdG8gbW9udGggYW5kIG1ha2UgaXQgZmFjdG9yIHVzaW5nIGFzX2ZhY3Rvcg0KICAgICAgICAgeWVhciA9IGFzLmZhY3Rvcih5ZWFyKGRhdGUpKSkgJT4lDQogIG11dGF0ZShwbV9sdiA9IGN1dChhdmdfcG0sIA0KICAgICAgICAgYnJlYWtzID0gYygwLDEyLDM1LjQsNTUuNCwxNTAuNCwyNTAuNCwzNTAuNCksDQogICAgICAgICBsYWJlbHMgPSBjKCdnb29kJywnbW9kZXJhdGUnLCd1bmhlYXRoeSBmb3Igc2Vuc2l0aXZlIGdyb3VwcycNCiAgICAgICAgICAgICAgICAgICAgLCd1bmhlYXRoeScsJ3ZlcnkgdW5oZWF0aHknLCdoYXphcmRvdXMnKSkpICU+JQ0KICBnZ3Bsb3QoYWVzKGRheSwgeT1tb250aCwgZmlsbD1wbV9sdikpICsgDQogIHhsYWIoIkRheSIpICsgeWxhYigiTW9udGgiKSArDQogIGdlb21fdGlsZShjb2xvcj0id2hpdGUiKSArDQogIGZhY2V0X2dyaWQoeWVhcn4uKSArIA0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCdncmVlbicsJ3llbGxvdycsJ29yYW5nZScsJ3JlZCcsJ21hZ2VudGEnLCAndmlvbGV0JykpKw0KICBjb29yZF9lcXVhbCgpDQpgYGANCiMjIGFkZCB0b29sdGlwDQoNCmBgYHtyfQ0KdHQgPC0gZGYgJT4lICAjIyBwdXQgZ2dwbG90IGluIHRvIHR0ICh0b29sdGlwKQ0KICBmaWx0ZXIoeWVhcihsb2NhbF9kdCkgPiAyMDE3KSAlPiUNCiAgbXV0YXRlKGRhdGUgPSBkYXRlKGxvY2FsX2R0KSkgJT4lDQogIGdyb3VwX2J5KGRhdGUpICU+JQ0KICBzdW1tYXJpemUoYXZnX3BtID0gbWVhbihQTTJfNSkpICU+JQ0KICBtdXRhdGUoZGF5ID0gYXMuZmFjdG9yKGRheShkYXRlKSksDQogICAgICAgICBtb250aCA9IGZjdF9yZXYoYXNfZmFjdG9yKG1vbnRoKGRhdGUpKSksICANCiAgICAgICAgIHllYXIgPSBhcy5mYWN0b3IoeWVhcihkYXRlKSkpICU+JQ0KICBtdXRhdGUocG1fbHYgPSBjdXQoYXZnX3BtLCANCiAgICAgICAgIGJyZWFrcyA9IGMoMCwxMiwzNS40LDU1LjQsMTUwLjQsMjUwLjQsMzUwLjQpLA0KICAgICAgICAgbGFiZWxzID0gYygnZ29vZCcsJ21vZGVyYXRlJywndW5oZWF0aHkgZm9yIHNlbnNpdGl2ZSBncm91cHMnDQogICAgICAgICAgICAgICAgICAgICwndW5oZWF0aHknLCd2ZXJ5IHVuaGVhdGh5JywnaGF6YXJkb3VzJykpKSAlPiUNCiAgZ2dwbG90KGFlcyhkYXksIHk9bW9udGgsIGZpbGw9cG1fbHYpKSArIA0KICB4bGFiKCJEYXkiKSArIHlsYWIoIk1vbnRoIikgKw0KICBnZW9tX3RpbGUoY29sb3I9IndoaXRlIikgKw0KICBmYWNldF9ncmlkKHllYXJ+LikgKyANCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygnZ3JlZW4nLCd5ZWxsb3cnLCdvcmFuZ2UnLCdyZWQnLCdtYWdlbnRhJywgJ3Zpb2xldCcpKSsNCiAgY29vcmRfZXF1YWwoKQ0KDQpnZ3Bsb3RseSh0dCkgIyBkaXNwbGF5IGdncGxvdCB3aXRoIHRvb2x0aXANCmBgYA0KDQo=